﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using Util;
using Microsoft.VisualStudio.Text.Editor;
using Logging;
using Microsoft.VisualStudio.Text;
using System.Windows.Interop;
using System.Drawing;

namespace UWCSE.CompleterVSPackage
{
    /// <summary>
    /// Interaction logic for QueryWindow.xaml
    /// </summary>
    public partial class QueryControl : UserControl
    {
        private readonly CollectionViewSource filteredResults;
        private readonly CompletionsGuiHelper guiHelper;
        private readonly QueryForm form;
        private readonly string query;

        internal QueryControl(ProcessHandler processHandler,
            ObjectBox<IWpfTextView> activeWpfTextViewBox,
            string query = null, Span? matchSpan = null)
        {
            InitializeComponent();

            this.query = query;
            this.guiHelper = new GuiHelper(processHandler, resultsListBox, activeWpfTextViewBox, this, matchSpan);

            this.filteredResults = new CollectionViewSource();
            this.filteredResults.Source = guiHelper.Results;
            this.filteredResults.Filter += filterResults;

            resultsListBox.ItemsSource = filteredResults.View;

            this.form = new QueryForm(this);
        }

        private string InputQuery { get { return query; } }

        void filterResults(object sender, FilterEventArgs e)
        {
            var filters = filterTextBox.Text.Split(' ');
            var result = e.Item as Result;
            if (result == null)
            {
                e.Accepted = true;
            }
            else
            {
                string resLower = result.CompletionText.ToLower();
                string resOnlyUpper = new string(result.CompletionText.Where(c => !Char.IsLower(c)).ToArray());
                e.Accepted = filters.Where(f => f.Length > 0)
                                    .All(f => resLower.Contains(f.ToLower())
                                         || (f.All(Char.IsUpper) && resOnlyUpper.Contains(f)));
            }
        }

        public string Query { set { queryLabel.Content = value; } }

        private class GuiHelper : CompletionsGuiHelper
        {
            private readonly QueryControl qw;

            internal GuiHelper(ProcessHandler processHandler, ListBox resultsList,
                               ObjectBox<IWpfTextView> activeWpfTextViewBox,
                               QueryControl qw, Span? matchSpan)
                : base(processHandler, resultsList, activeWpfTextViewBox, matchSpan)
            {
                this.qw = qw;
            }

            internal override void Show()
            {
                qw.ShowQueryWindow();
                Logger.PrintLineWithTime("Query window visible");
            }

            internal override void BeforeShow() { }

            internal override void NotPartialExpression()
            {
                string query;
                if (qw.InputQuery != null)
                {
                    query = qw.InputQuery;
                }
                else
                {
                    Span span;
                    if (!MatchSpan.HasValue || MatchSpan.Value.Length == 0)
                    {
                        span = activeWpfTextViewBox.Contents.Selection.StreamSelectionSpan.SnapshotSpan.Span;
                    }
                    else
                    {
                        span = MatchSpan.Value;
                    }
                    char[] queryArr = new char[span.Length];
                    activeWpfTextViewBox.Contents.TextSnapshot.CopyTo(span.Start, queryArr, 0, span.Length);
                    query = new string(queryArr);
                }
                qw.HideQueryWindow();
                qw.OpenEnterQueryForm(query);
            }

            internal override Span HandleRewriteInfo(Span matchSpan, string matchText, string rewrite)
            {
                qw.Query = rewrite;
                return matchSpan;
            }

            internal override void FinishedReplace()
            {
                qw.form.Hide();
            }

            internal override void NotifyMatchType(string typeStr)
            {
                qw.TypeString = typeStr;
            }
        }

        private void resultsListBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            if (!e.TextComposition.Text.Equals("\r"))
            {
                var copiedEvent = new TextCompositionEventArgs(e.Device, e.TextComposition);
                copiedEvent.RoutedEvent = e.RoutedEvent;

                filterTextBox.RaiseEvent(copiedEvent);
                filterTextBox.BeginChange();
                filterTextBox.AppendText(e.TextComposition.Text
                    .Replace('\n', ' ').Replace('\r', ' '));
                filterTextBox.CaretIndex = filterTextBox.Text.Length + 1;
                filterTextBox.EndChange();
                e.Handled = true;
            }
        }

        public void ShowQueryWindow()
        {
            form.Show();
            resultsListBox.Focus();
        }
        public void HideQueryWindow()
        {
            form.Hide();
        }

        private static readonly Key[] resultsListKeys = new Key[] {
            Key.Up, Key.Down, Key.PageDown, Key.PageUp, Key.Enter, Key.Escape
        };
        private void resultsListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (!resultsListKeys.Contains(e.Key))
            {
                filterTextBox.RaiseEvent(e);
            }
            else if (e.Key.Equals(Key.Enter))
            {
                guiHelper.DoReplace();

                guiHelper.CancelCompletion();
                form.Hide();
            }
            else if (e.Key.Equals(Key.Escape))
            {
                guiHelper.CancelCompletion();
                form.Hide();
            }
        }

        private void filterTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            var sel = resultsListBox.SelectedItem;
            filteredResults.View.Refresh();
            resultsListBox.SelectedItem = sel;
        }

        internal void DoCompletionAction()
        {
            guiHelper.DoCompletionAction(query);
        }

        private void resultsListBox_TextInput(object sender, TextCompositionEventArgs e)
        {
            filterTextBox.RaiseEvent(e);
        }

        private void resultsListBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (!resultsListKeys.Contains(e.Key))
            {
                filterTextBox.RaiseEvent(e);
            }
        }

        private void resultsListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            guiHelper.DoReplace();

            guiHelper.CancelCompletion();
            form.Hide();
        }

        private string typeString = null;
        public string TypeString
        {
            set
            {
                typeString = value;
                guiHelper.Dispatcher.InvokeIfRequired(() =>
                    typeLabel.Content = "Type: " + value,
                    System.Windows.Threading.DispatcherPriority.DataBind);
            }
        }

        private void resultsList_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            guiHelper.resultsList_ScrollChanged();
        }

        internal void NotifyFormClosed()
        {
            guiHelper.CancelCompletion();
        }

        private void clearButton_Click(object sender, RoutedEventArgs e)
        {
            filterTextBox.Clear();
        }

        private void queryButton_Click(object sender, RoutedEventArgs e)
        {
            OpenEnterQueryForm();
        }
        public void OpenEnterQueryForm(string currentQuery = null)
        {
            if (currentQuery == null)
            {
                currentQuery = queryLabel.Content.ToString();
            }
            if (!currentQuery.StartsWith("(")
                && typeString != null
                && !typeString.Equals("Type: object")
                && typeString.Length > 0)
            {
                currentQuery = "(" + typeString + ") " + currentQuery;
            }
            new EnterQueryForm(newQuery =>
            {
                new QueryControl(guiHelper.processHandler,
                    guiHelper.activeWpfTextViewBox, newQuery, guiHelper.MatchSpan
                        ?? guiHelper.activeWpfTextViewBox.Contents.Selection.StreamSelectionSpan.SnapshotSpan.Span)
                    .DoCompletionAction();
            }, () => { }, currentQuery).Show();
        }
    }
}
